iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 13
0
自我挑戰組

React 30 天學習歷程系列 第 13

【Day 13】React 元件生命週期(二):新版 React 生命週期方法

  • 分享至 

  • xImage
  •  

React 在 16.3 版新增了一些新的生命週期方法,並開始逐漸廢棄一些舊方法,舊方法就是前一篇提過的
componentWillMount()
componentWillReceiveProps()
componentWillUpdate()
新增的生命週期方法則是以下這幾個
getDerivedStateFromProps
getSnapshotBeforeUpdate
這一篇會針對整個生命週期和新版方法做整理。

初始化階段

  1. constructor(props)
    元件初始化階段第一件事就是調用 constructor(props) 並生成實例, props 就是元件的父層傳遞來的參數。同時元件也會藉由 super() 去繼承一些 React.Component 的屬性和方法。如下面的範例, Input 元件中的 constructor(props)props 裡面的參數就是 Form 傳入的參數,而使用 super() 後,this 會指向 Input 本身。不過使用 constructor() 的麻煩就是你的屬性、函式都得在裡面定義。在一些框架中如 create-react-app 中是可以在 class 中直接定義屬性和函式而不必透過 constructor()
class Form extends React.Component {
    state = {
        value: '123'
    }
    
    onEditer (value) {
        this.setState({value})
    }
    
    render () {
        const { value } = this.state
        return <div>
            {value}
            <Input value={value} onEditer={this.onEditer.bind(this)} />
        </div>
    }
}
class Input extends React.Component {
    constructor (props) {
        super() // 改變 this 的指向為這個元件
        console.log(props) // props 就是 Form 元件傳進來的物件,裏面包含了 value、
    }
    render () {
        const { onEditer } = this.props;
        return <div>
            <input type="text" onChange={e => onEditer(e.target.value)} />
        </div>
    }
}
  1. static getDerivedStateFromProps(props, state)
    constructor() 執行完畢後就會接著執行 getDerivedStateFromProps(),或者是元件有更新時,像是 propsstate 更新,或者 forceUpdate() 執行,都會觸發這個方法。getDerivedStateFromProps() 是一個靜態方法,必須搭配 static 使用,它接受兩個參數,propsstate,我們可以透過這個方法,去監聽這兩個參數,執行一些事情。getDerivedStateFromProps() 最後必須返回一個物件或是 null 值,如果返回物件,則依照物件內的屬性來更新 static
class Form extends React.Component {
    state = {
        value: 'leo'
    }
    
    onEditer (value) {
        this.setState({value})
    }
    
    render () {
        const { value } = this.state
        return <div>
            {value}
            <Input value={value} onEditer={this.onEditer.bind(this)} />
        </div>
    }
}
class Input extends React.Component {
    state = {
        value: 'jack'
    }

    static getDerivedStateFromProps(props, state) {
        if (props.value !== state.value) {
            return {
                value: props.value,
                age: 26 //這邊新增一個 age 屬性,也會增加在 state 裡面
            }
        }
        return null
    }

    render () {
        console.log(this.state) // value: leo, age: 26
        const { onEditer, value } = this.props;
        return <div>
            <input type="text" value={value} onChange={e => onEditer(e.target.value)} />
        </div>
    }
}
  1. render
    getDerivedStateFromProps() 執行完後,就接著執行 render 渲染整個元件。
  2. componentDidMount
    render 執行完後,表示我們已經有了整個 DOM 結構,這時候才輪到 componentDidMount 執行,而且只會在整個結構第一次渲染完成的這一次執行。componentDidMount 沒有任何參數,一般會在這個方法中去執行數據的請求。
class UserProfile extends React.Component {
    state = {
        user: null
    }
    
    //在 componentDidMount 中進行非同步請求,並更新 state
    async componentDidMount() {
        const user = await fetch('/api/user').then(res => json());
        this.setState({ user })
    }
    
    render () {
        const { user = {} } = this.state;
        return <div>
            name: {user.name}
        </div>
    }
}

更新階段

  1. static getDerivedStateFromProps(props, state)
    如初始化階段所提,propsstate 更新,或者 forceUpdate() 執行,都會觸發這個方法。
  2. shouldComponentUpdate()
    shouldComponentUpdate() 接在 getDerivedStateFromProps(props, state) 之後執行,主要是藉由返回 Boolean 來判斷是否更新元件,避免一些不必要的更新。不過 React 官方不建議使用這個方法,而是建議使用 PureComponent,因為 PureComponent 也會避免不必要的更新。
class UserProfile extends React.Component {
    state = {
        name: 'leo',
        age: 20
    }
    
    changeVlue (state) {
        return new Promise((resolve) => {
            this.setState(state, resolve)
        })
    }

    async changeData () {
        const { age } = this.state
        await this.changeVlue({age: age + 1})
    }

    shouldComponentUpdate () {
        if (this.state.age > 26) { //當 age 大於 26,return false,停止更新
            return false
        }
        return true
    } 
    
    render () {
        const { name, age } = this.state;
        return <div>
            {name} {age} 歲
            <button onClick={this.changeData.bind(this)}>+1</button>
        </div>
    }
}
  1. render
    同初始化階段的 render。
  2. getSnapshotBeforeUpdate
    getSnapshotBeforeUpdate 是在 render 執行後,DOMrefs 更新前所做的事情,這可以讓我們在 DOM 變化前獲取一些訊息,例如畫面滾動之類的。如果在 getSnapshotBeforeUpdate 中返回值,生命週期會讓這個值作為參數傳遞給 componentDidUpdate()
  3. componentDidUpdate(prevProps, prevState, snapshot)
    元件更新完畢後執行,這個方法中的三個參數分別為更新前的 props(prevProps)、更新前的 state(prevState)、以及 getSnapshotBeforeUpdate 返回的參數。

卸載階段

  1. componentWillMount
    componentWillMount() 是卸載階段唯一的方法,是在元件卸載前執行,我們可以在這邊處理一些不必在執行的功能,避免過度使用記憶體,例如計時器功能。

錯誤處理

  1. componentDidCatch
    componentDidCatch() 是在元件有錯誤時候會執行。

元件的其他屬性

  1. defaultProps
    defaultProps 用於定義 props 的默認屬性,可以用在定義 undefinedprops,但不適用於 null。有分外部定義及內部定義兩種,外部定義用法如下,利用 compnentName.defaultProps 來定義默認屬性,當沒有外部屬性傳入,就使用默認屬性。
class Form extends React.Component {
    state = {
        value: 'leo'
    }
    
    onEditer (value) {
        this.setState({value})
    }
    
    render () {
        const { title } = this.props //'測試用 input'
        const { value } = this.state
        return <div>
            {value}
            {title}
            <Input value={value} onEditer={this.onEditer.bind(this)} />
        </div>
    }
}

// 外部定義 default
Form.defaultProps = {
    title: '測試用 input'
}

內部定義則是直接使用 static defaultProps 來定義

class Form extends React.Component {
    state = {
        value: 'leo'
    }

    static defaultProps = { //由於 defaultProps 是靜態方法,必須搭配 static 使用
        title: '默認 props'
    }
    
    onEditer (value) {
        this.setState({value})
    }
    
    render () {
        const { title } = this.props
        const { value } = this.state
        return <div>
            {value}
            {title}
            <Input value={value} onEditer={this.onEditer.bind(this)} />
        </div>
    }
}

當內部外部都有定義 defaultProps 時候,只會執行外部定義,而會忽略內部定義。
2. forceUpdate()
forceUpdate() 這個方法會強制渲染 React 元件,包含子元件也會重新渲染,使用方式是直接調用,如下

class App extends React.Component {
    componentDidMount () {
        this.forceUpdate();
    }
}

小結

這一篇我們整理了新版 React 生命週期方法,到這篇為止,算是把 React 中的一些基礎概念整理完畢,下一篇開始,將會進行一些實作練習。


上一篇
【Day 12】React 元件生命週期(一):簡介 React 生命週期及舊版 React 生命週期方法
下一篇
【Day 14】個人網站實作(一):create-react-app + 套件安裝及版面布置
系列文
React 30 天學習歷程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言